<script setup lang="ts">
import { onMounted, reactive, ref } from 'vue';
import { saveAs } from 'file-saver';
import * as htmlDocx from 'html-docx-js-typescript';
import mammoth from 'mammoth';
import _ from 'underscore';
import { useDialog } from 'naive-ui';
import { escapeHTMLManually, getImageInfoFromBase64, sliceStringByLength, successMessage } from '@/utils/utils';
import {
  analysisUpdateFileAction,
  getTemplateFileAction,
  getUsedCount,
  htmlToDocAction,
  msgCheckAction
} from '@/service/api';
import { useRouterPush } from '@/hooks/common/router';
import { localStg } from '@/utils/storage';

interface Props {
  analysisType?: number; // 1.新建；2.编辑（查看）
  logId?: number;
  docFilePath?: string;
}

const props = withDefaults(defineProps<Props>(), {
  analysisType: 1,
  logId: 0,
  docFilePath: ''
});

const { routerPushByKey } = useRouterPush();

const showLoading = ref<boolean>(true);
const loadingDes = ref<string>('文档较大，正在努力处理中，请稍后...');

const contentHtml = ref<string>('');
const docContentHtml = ref<string>('');
const imgList = ref<any[]>([]);
const IMAGE_TAG: string = 'IMAGE_TAG';

const currentIndex = ref<number>(0);
const currentFileObj = ref<any>();

onMounted(() => {
  getUsedCountNumber();
});

const usedCount = ref<number>(0);

// 获取生成标书的使用剩余数
async function getUsedCountNumber() {
  const params: any = {
    moduleId: localStg.get('moduleId') || 1,
    applicationId: 1
  };
  const { data, error } = await getUsedCount(params);
  if (!error) {
    usedCount.value = data;
  }
}

// 文件列表
const fileList = ref<any[]>([]);

// 获取所有模板文件
async function getDocumentTemplateData() {
  const params: any = {
    logId: props.logId
  };
  const { data, error } = await getTemplateFileAction(params);
  if (!error) {
    fileList.value = data;
    if (fileList.value.length > 0) {
      currentFileObj.value = fileList.value[currentIndex.value];
      // 编辑器初始化成功后再获取文档内容
      getHtmlByDocument(fileList.value[currentIndex.value].filePath);
    }
  }
}

// 编辑器配置
const fileEditorConfig: object = reactive({
  serverUrl: '',
  UEDITOR_HOME_URL: '/modules/bidWriting/UEditorPlus/',
  UEDITOR_CORS_URL: '/modules/bidWriting/UEditorPlus/',
  toolbars: [],
  autoFloatEnabled: false,
  readonly: props.analysisType !== 1,
  autoSaveEnable: false, // 禁用自动保存
  elementPathEnabled: false, // 禁用元素路径
  initialFrameHeight: '633',
  autoHeightEnabled: false, // 禁止编辑器自动长高
  maximumWords: 100000
});

// 引入的js文件，这里是一定要写的。
const fileEditorDependencies: string[] = reactive(['ueditor.config.js', 'ueditor.all.js', 'lang/zh-cn/zh-cn.js']);

const ueditor = ref<any | null>(null);

function fileReadyHandle(editor: any) {
  ueditor.value = editor;

  // 监听内容变化
  editor.addListener('contentchange', () => {
    // console.log('编辑器内容发生改变');
  });

  // 设置编辑器编辑状态
  // ueditor.value.readonly = props.analysisType !== 1;

  getDocumentTemplateData();
}

// 根据文档获取html内容
function getHtmlByDocument(filePath: string) {
  const { VITE_OTHER_SERVICE_BASE_URL } = import.meta.env;
  const otherUrl = JSON.parse(VITE_OTHER_SERVICE_BASE_URL);
  const docPath: string = otherUrl.fileUrl + filePath;
  loadingDes.value = '文档较大，正在努力处理中，请稍后...';
  showLoading.value = true;
  fetch(docPath, { cache: 'no-cache' })
    .then(response => response.blob())
    .then(blobData => {
      let file: File | null = new File([blobData], `${currentFileObj.value.fileName}.docx`, {
        type: 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
        lastModified: Date.now()
      });

      analysisWord(file).then(html => {
        // console.log("html: ", html);
        // console.log('imgList:', imgList.value);
        for (let i: number = 0; i < imgList.value.length; i++) {
          docContentHtml.value = docContentHtml.value.replace(IMAGE_TAG, imgList.value[i]);
        }
        docContentHtml.value = docContentHtml.value.replace(/style=""/g, '');
        // 移除第一行的空行（样式解析后，空行被p标签包裹）
        docContentHtml.value = docContentHtml.value.replace(
          /<p style="text-align: left; line-height: 150%; align-items: baseline;"><\/p>/g,
          ''
        );
        // console.log("docContentHtml.value: ", docContentHtml.value);
        // 插入文本
        ueditor.value.setContent(docContentHtml.value);
        // ueditor.value.execCommand('inserthtml', docContentHtml.value);
        // 设置内容光标到内容最前面
        ueditor.value.focus();
        showLoading.value = false;
        // nextTick(() => {
        //   saveContentToService(1);
        // });
      });

      // 文件信息置空
      file = null;
    })
    .catch(error => {
      console.error('发生错误：', error);
      errorMessage('获取文档失败');
      showLoading.value = false;
    });
}

// 解析word文件，将word转为html
function analysisWord(file: any) {
  return new Promise(resolve => {
    const reader = new FileReader();
    reader.onload = evt => {
      // console.log('evt：', evt);
      // 当文件读取完毕后
      // p标签首行没有缩进
      const options = {
        transformDocument: transformElement,
        styleMap: ["p[style-name='Section Title'] => h1:fresh", "p[style-name='Subsection Title'] => h2:fresh"]
      };

      // 调用mammoth组件的方法进行解析文件
      mammoth
        .convertToHtml({ arrayBuffer: evt.target!.result }, options)
        .then(async resultObject => {
          // console.log("resultObject: ", resultObject);
          const imgSrcReg = /<img[^>]+src=[\\'|\\"]?([^(?"|?'|?>)]+)[\\'|\\"]?[^>]*>/gim;
          const imgSrcList = resultObject.value.match(imgSrcReg);

          for (let i: number = 0; i < imgSrcList?.length; i++) {
            const imgSrc: string = imgSrcList[i].match(/src="(.+?)"/)[1];

            // console.log('imgSrc: ', imgSrc);
            const info = await getImageInfoFromBase64(imgSrc);
            if (info) {
              imgSrcList[i] =
                `${imgSrcList[i].substring(0, imgSrcList[i].length - 2)}width="${info.width}" height="${info.height}" ${imgSrcList[i].substring(imgSrcList[i].length - 2)}`;
            } else {
              imgSrcList[i] =
                `${imgSrcList[i].substring(0, imgSrcList[i].length - 2)}width="90%" height="auto" ${imgSrcList[i].substring(imgSrcList[i].length - 2)}`;
            }
          }

          if (imgSrcList) {
            imgList.value = imgSrcList;
          }

          // console.log('imgList.value: ', imgList.value);
          resolve(resultObject.value); // 将处理好的html数据返回
          reader.abort(); // 关闭读取
        })
        .catch(err => {
          console.log('err: ', err);
          reader.abort(); // 关闭读取
        });
    };
    reader.readAsArrayBuffer(file);
    // 启动读取指定的 Blob 或 File 内容：https://developer.mozilla.org/zh-CN/docs/Web/API/FileReader/readAsArrayBuffer
  });
}

/**
 * transformElement 和 transformParagraph 是用于将文档转换为HTML的。 用于文档转换的API是不稳定的，并且可能在任何版本之间发生变化。
 * 如果你依赖这种行为，你应该固定到mammath.js的特定版本，并在更新前仔细测试。
 */
// 元素重新定义
function transformElement(element: any) {
  if (element.children) {
    const children: any = _.map(element.children, transformElement);
    element = { ...element, children };
  }

  if (element.type === 'paragraph') {
    element = transformParagraph(element);
  }

  if (element.type === 'document') {
    // docParagraphList.value = element.children;
    // console.log('docParagraphList.value: ', element.children);
    htmlFormat(element.children);
  }

  return element;
}

// 段落内容重新定义
function transformParagraph(element: any) {
  if (element.alignment === 'center' && !element.styleId) {
    return { ...element, styleId: 'Heading2' };
  }
  return element;
}

// 根据文档解析API进行HTML内容拼接封装
function htmlFormat(docParagraphList: any[]) {
  let docHtmlStr: string = '';
  for (let i: number = 0; i < docParagraphList.length; i++) {
    const paragraphObj: any = docParagraphList[i];
    if (paragraphObj.type === 'paragraph') {
      // 普通段落内容（包括文字和图片）
      let paragraphStr: string = '';
      let alignmentStr: string = '';
      if (paragraphObj.alignment) {
        alignmentStr = `text-align: ${paragraphObj.alignment}; line-height: 150%; align-items: baseline;`;
      } else {
        alignmentStr = 'text-align: left; line-height: 150%; align-items: baseline;';
      }

      for (let j: number = 0; j < paragraphObj.children.length; j++) {
        if (paragraphObj.indent && paragraphObj.indent.firstLine) {
          alignmentStr += 'text-indent: 2em;';
        }
        const formatContent = paragraphHtmlFormat(paragraphObj.children[j]);
        if (formatContent === IMAGE_TAG) {
          alignmentStr = 'display: block; text-align: center;';
        }
        paragraphStr += formatContent;
      }
      if (paragraphObj.styleId === 'Heading1' || paragraphObj.styleName === 'heading 1') {
        docHtmlStr += `<h1 style="${alignmentStr}">${paragraphStr}</h1>`;
      } else if (paragraphObj.styleId === 'Heading2' || paragraphObj.styleName === 'heading 2') {
        docHtmlStr += `<h2 style="${alignmentStr}">${paragraphStr}</h2>`;
      } else if (paragraphObj.styleId === 'Heading3' || paragraphObj.styleName === 'heading 3') {
        docHtmlStr += `<h3 style="${alignmentStr}">${paragraphStr}</h3>`;
      } else if (paragraphObj.styleId === 'Heading4' || paragraphObj.styleName === 'heading 4') {
        docHtmlStr += `<h4 style="${alignmentStr}">${paragraphStr}</h4>`;
      } else if (paragraphObj.styleId === 'Heading5' || paragraphObj.styleName === 'heading 5') {
        docHtmlStr += `<h5 style="${alignmentStr}">${paragraphStr}</h5>`;
      } else if (paragraphObj.styleId === 'Heading6' || paragraphObj.styleName === 'heading 6') {
        docHtmlStr += `<h6 style="${alignmentStr}">${paragraphStr}</h6>`;
      } else {
        docHtmlStr += `<p style="${alignmentStr}">${paragraphStr}</p>`;
      }
    } else if (paragraphObj.type === 'table') {
      // 表格内容处理
      const tableObj: any = docParagraphList[i];
      docHtmlStr += tableHtmlFormat(tableObj);
    }
  }
  docContentHtml.value = `<div style="margin: 72pt;">${docHtmlStr}</div>`;
}

// 段落内容格式化
function paragraphHtmlFormat(paragraphObj: any) {
  let contentStr: string = '';

  // console.log("paragraphObj: ", paragraphObj);

  if (!paragraphObj.children) {
    return '<span></span>';
  }

  if (paragraphObj.children.length === 0) {
    return '<br/>';
  }

  for (let i: number = 0; i < paragraphObj.children.length; i++) {
    if (paragraphObj.children[i].type === 'text') {
      let textStr: string = paragraphObj.children[i].value;
      // 如果有html字符串，将其解析为html字符串，就可以正常展示
      textStr = escapeHTMLManually(textStr);

      let styleStr: string = `line-height: 150%;`;
      if (paragraphObj.font) {
        styleStr += `font-family: ${paragraphObj.font};`;
      }
      if (paragraphObj.fontSize) {
        styleStr += `font-size: ${paragraphObj.fontSize}pt;`;
      }
      // 是否有下划线
      if (paragraphObj.isUnderline) {
        styleStr += 'text-decoration: underline;';
      }
      // 是否有删除线
      if (paragraphObj.isStrikethrough) {
        styleStr += 'text-decoration: line-through;';
      }
      // if (paragraphObj.verticalAlignment) {
      //   styleStr += `display: block; align-items: ${paragraphObj.verticalAlignment};`;
      // }
      // 是否加粗(使用 strong 标签加粗内容)
      if (paragraphObj.isBold) {
        styleStr += 'font-weight: bold;';
      }
      contentStr += `<span style="${styleStr}">${textStr}</span>`;
    } else if (paragraphObj.children[i].type === 'break') {
      // 换行
      contentStr += '<br/>';
    } else if (paragraphObj.children[i].type === 'image') {
      // 图片
      contentStr += IMAGE_TAG;
    } else {
      contentStr = String(contentStr);
    }
  }

  // return `<span><span>${contentStr}</span></span>`;
  return contentStr;
}

// 表格内容格式化
function tableHtmlFormat(tableObj: any) {
  let tableStr: string = '';
  const tableStyle: string = '';
  for (let i: number = 0; i < tableObj.children.length; i++) {
    // 遍历 tableRow
    const rowObj: any = tableObj.children[i];
    if (rowObj.isHeader) {
      tableStr += '<thead><tr>';
    } else {
      tableStr += '<tbody><tr>';
    }
    for (let j: number = 0; j < rowObj.children.length; j++) {
      // 遍历 tableCell
      const cellObj: any = rowObj.children[j];
      if (rowObj.isHeader) {
        tableStr += `<th style="${tableStyle}">`;
      } else {
        tableStr += `<td>`;
      }
      for (let k: number = 0; k < cellObj.children.length; k++) {
        const contentObj: any = cellObj.children[k];

        let paragraphStr: string = '';
        let alignmentStr: string = '';
        if (contentObj.alignment) {
          alignmentStr = `text-align: ${contentObj.alignment};`;
        } else {
          alignmentStr = 'text-align: left;';
        }
        for (let l: number = 0; l < contentObj.children.length; l++) {
          if (contentObj.children.length > 1 && l === 0 && contentObj.indent && contentObj.indent.firstLine) {
            alignmentStr += 'text-indent: 2em;';
          }
          paragraphStr += paragraphHtmlFormat(contentObj.children[l]);
        }
        tableStr += `<p style="${alignmentStr}">${paragraphStr}</p>`;
      }
      if (rowObj.isHeader) {
        tableStr += '</th>';
      } else {
        tableStr += '</td>';
      }
    }
    if (rowObj.isHeader) {
      tableStr += '</tr></thead>';
    } else {
      tableStr += '</tr></tbody>';
    }
  }
  return `<table style="width: 100%; border-collapse: collapse;">${tableStr}</table>`;
}

// 文件列表点击
function fileListClick(item: any, index: number) {
  currentIndex.value = index;
  currentFileObj.value = item;

  // 编辑器初始化成功后再获取文档内容
  getHtmlByDocument(item.filePath);
}

// 编辑器内容处理
async function ueditorContentHandle() {
  let htmlContentStr: string = ueditor.value.getContent();

  const imgSrcReg = /<img[^>]+src=[\\'|\\"]?([^(?"|?'|?>)]+)[\\'|\\"]?[^>]*>/gim;
  const imgSrcList = htmlContentStr.match(imgSrcReg);

  const maxWidth: number = 590;
  const maxHeight: number = 840;

  for (let i: number = 0; i < imgSrcList?.length; i++) {
    const imgString: string = imgSrcList[i];

    const imgSrc: string = imgString.match(/src="(.+?)"/)[1];

    /** html转word中的图片尺寸处理 */
    /**
     * 方案一：（图片标签本身要有宽高属性、需要考虑宽高是百分比及auto的情况）
     *
     * 1. 通过图片标签字符转转dom节点获取图片的基本信息；
     * 2. 获取图片的宽高比，然后根据A4纸的尺寸计算出图片应该缩放的比例；
     * 3. 根据比例计算出图片应该缩放后的宽度和高度，并设置给img标签；
     * 4. 将dom节点转换为字符串，替换原html中的img标签；
     */

    /**
     * 方案二：（通用方案）
     *
     * 1. 根据图片的base64字符串获取图片的基本信息；
     * 2. 然后根据A4纸的尺寸计算出图片的缩放尺寸；
     * 3. 创建img标签节点，设置宽高等属性；
     * 4. 使用img标签的innerHTML替换原html中的img标签；
     */

    const info = await getImageInfoFromBase64(imgSrc);
    if (info) {
      if (info.width > maxWidth || info.height > maxHeight) {

        // A4纸在边距为25mm的情况下，可用区域为453px * 692px
        let newWidth: number = maxWidth;
        let newHeight: number = maxHeight;

        // 原图片宽高比例 大于 指定的宽高比例，这就说明了原图片的宽度必然 > 高度
        if (info.width / info.height >= maxWidth / maxHeight) {
          if (info.width > maxWidth) {
            newWidth = maxWidth;
            // 按原图片的比例进行缩放
            newHeight = (info.height * maxWidth) / info.width;
          } else {
            // 按原图片的大小进行缩放
            newWidth = info.width;
            newHeight = info.height;
          }
        } else {
          // 原图片的高度必然 > 宽度
          if (info.height > maxHeight) {
            newHeight = maxHeight;
            // 按原图片的比例进行缩放
            newWidth = (info.width * maxHeight) / info.height;
          } else {
            // 按原图片的大小进行缩放
            newWidth = info.width;
            newHeight = info.height;
          }
        }

        htmlContentStr = htmlContentStr.replace(
          imgString,
          `<img src='${imgSrc}' width='${newWidth}' height='${newHeight}' alt=''/>`
        );
      } else {
        htmlContentStr = htmlContentStr.replace(
          imgString,
          `<img src='${imgSrc}' width='${info.width}' height='${info.height}' alt=''/>`
        );
      }
    }
  }

  // 设置表格边框
  return htmlContentStr.replaceAll(/<table/g, `<table border-collapse="collapse" border="1" `);
}

const dialog = useDialog();

// 保存当前信息
async function saveCurrentContentClick() {
  const checkParams: any = {
    moduleValue: localStg.get('moduleId') || 1,
    keyword: ueditor.value.getContentTxt()
  };

  const { data: checkData, error: checkError } = await msgCheckAction(checkParams);
  if (!checkError) {
    if (checkData && checkData.length > 0) {
      dialog.error({
        title: '温馨提示',
        content: `标签名称中包含以下敏感词：\n${checkData.join('，')}`,
        style: 'white-space: pre-line;',
        positiveText: '确认'
      });
      return;
    }
  }

  // 设置表格边框
  const htmlContent: string = await ueditorContentHandle();

  const saveParams: any = {
    contentList: sliceStringByLength(`<div>${htmlContent}</div>`, 40000)
  };

  const { data: saveData, error: saveError } = await htmlToDocAction(saveParams);
  if (!saveError) {
    const params: any = {
      logId: props.logId,
      id: currentFileObj.value.id, // 必传
      filePath: saveData
    };
    const { data, error } = await analysisUpdateFileAction(params);
    if (!error) {
      successMessage('文档保存成功');

      await getDocumentTemplateData();
    }
  }
}

// 导出下载
async function downloadClick() {
  // 设置表格边框
  const htmlContent: string = await ueditorContentHandle();

  const formatContent: string = `
    	 	<!DOCTYPE html>
    	 	<html lang="en">
          <head>
              <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
              <title>${currentFileObj.value.fileName}</title>
          </head>
          <body>
             ${htmlContent}
          </body>
        </html>`;

  // console.log('formatContent: ', formatContent);

  const convertedFile = htmlDocx.asBlob(formatContent);

  // 生成链接并且下载
  convertedFile.then(fileData => {
    saveAs(fileData as Blob, `${currentFileObj.value.fileName}.docx`, { encoding: 'UTF-8' });
    successMessage('文档下载成功');
  });

  // const saveParams: any = {
  //   contentList: sliceStringByLength(`<div>${downloadContent}</div>`, 40000)
  // };
  //
  // // console.log('内容：', `<div>${htmlContent}</div>`);
  //
  // const { VITE_OTHER_SERVICE_BASE_URL } = import.meta.env;
  // const otherUrl = JSON.parse(VITE_OTHER_SERVICE_BASE_URL);
  //
  // const { data, error } = await htmlToDocAction(saveParams);
  // if (!error) {
  //   saveAs(otherUrl.fileUrl + data, `${currentFileObj.value.fileName}.docx`, { encoding: 'UTF-8' });
  //   successMessage('文档下载成功');
  // }
}

const isCreate = ref<boolean>(false);

// 生成标书
function createBidClick() {
  if (usedCount.value > 0) {
    dialog.info({
      title: '生成标书',
      content: '即将前往【立即智写】，我们会根据你的招标文件自动完成标书目录生成。确认后将退出【慧眼识标】功能模块。',
      positiveText: '确认',
      negativeText: '取消',
      onPositiveClick: () => {
        isCreate.value = true;

        const params: any = {
          remainTimes: usedCount.value,
          analysisId: props.logId,
          docFilePath: props.docFilePath
        };
        routerPushByKey('document_create', { query: params });
      }
    });
  } else {
    dialog.info({
      title: '剩余次数不足提醒',
      content: '小主，您已经没有剩余次数了哦~',
      positiveText: '确 定',
      onPositiveClick: () => {}
    });
  }
}
</script>

<!-- 模板填充 -->
<template>
  <div>
    <NSpace justify="end">
      <NButton class="top-btn" quaternary @click="downloadClick">
        <template #icon>
          <img src="@/assets/smart-eye/icon-module-download.png" width="20" alt="" />
        </template>
        导出下载
      </NButton>
      <NButton v-if="analysisType === 1" class="top-btn" quaternary @click="createBidClick">
        <template #icon>
          <img src="@/assets/smart-eye/icon-creat-bid.png" width="20" alt="" />
        </template>
        生成标书
      </NButton>
    </NSpace>
    <div class="content-box">
      <div class="left-box">
        <NScrollbar v-if="fileList.length > 0" class="max-h-620px">
          <div
            v-for="(item, index) in fileList"
            :key="item.id"
            class="left-item"
            :class="currentIndex === index ? 'left-item-active' : ''"
            @click="fileListClick(item, index)"
          >
            {{ item.fileName }}
          </div>
        </NScrollbar>
        <NEmpty v-else class="mt-260px" size="huge" description="暂无模板文件" />
      </div>
      <div v-if="isCreate" class="right-box z-1100 bg-white"></div>
      <div v-else class="right-box">
        <VueUeditorWrap
          v-model="contentHtml"
          editor-id="file_editor"
          :config="fileEditorConfig"
          :editor-dependencies="fileEditorDependencies"
          @ready="fileReadyHandle"
        />
        <NButton v-if="analysisType === 1" class="save-btn" type="primary" @click="saveCurrentContentClick">
          保存当前信息
        </NButton>
      </div>
    </div>
  </div>
</template>

<style scoped lang="scss">
.top-btn {
  width: 127px;
  height: 40px;
  background: rgba(255, 255, 255, 0.3);
  border-radius: 3px;
  border: 1px solid #ffffff;
  font-weight: 400;
  font-size: 16px;
  color: #333333;
  line-height: 40px;
}
.content-box {
  display: flex;
  flex-direction: row;
  width: 100%;
  height: 655px;
  background-color: white;
  border-radius: 4px;
  margin-top: 20px;
  .left-box {
    width: 315px;
    height: 100%;
    padding: 20px 0;
    border-right: 1px solid #e5e7eb;
    .left-item {
      width: 100%;
      padding: 0 20px;
      font-weight: 400;
      font-size: 18px;
      color: #333333;
      line-height: 42px;
      cursor: pointer;
      white-space: nowrap;
      overflow: hidden;
      text-overflow: ellipsis;
    }
    .left-item-active {
      background: #e6e2ff;
      color: #816cfe;
    }
  }
  .right-box {
    width: calc(100% - 293px);
    height: 100%;
    position: relative;
    .save-btn {
      position: absolute;
      bottom: 30px;
      right: 30px;
      font-size: 16px;
      height: 40px;
      z-index: 10000;
    }
  }
}
</style>
